package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.list.dao.GoodsDao;
import com.atguigu.gmall.list.model.*;
import com.atguigu.gmall.list.service.ListService;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuAttrValue;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author gsy
 * @date 2021年09月03日18:20
 * Description
 */
@Service
public class ListServiceImpl implements ListService {
    @Autowired
    private GoodsDao goodsDao;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;


    //下架
    @Override
    public void cancelSale(Long skuId) {

        goodsDao.deleteById(skuId);
    }

    //上架
    @Override
    public void onSale(Long skuId) {
        //添加文档记录
        Goods goods = new Goods();
        //查询skuInfo
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        goods.setId(skuId);
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());
        goods.setPrice(skuInfo.getPrice().doubleValue());
        goods.setTitle(skuInfo.getSkuName());
        Calendar calendar = Calendar.getInstance();
        goods.setCreateTime(calendar.getTime());
        //查询品牌信息
        BaseTrademark baseTrademark = productFeignClient.getBaseTrademark(skuInfo.getTmId());
        goods.setTmId(baseTrademark.getId());
        goods.setTmName(baseTrademark.getTmName());
        goods.setTmLogoUrl(baseTrademark.getLogoUrl());
        //查询分类信息
        BaseCategoryView baseCategoryView = productFeignClient.getCategory(skuInfo.getCategory3Id());
        goods.setCategory1Id(baseCategoryView.getCategory1Id());
        goods.setCategory1Name(baseCategoryView.getCategory1Name());
        goods.setCategory2Id(baseCategoryView.getCategory2Id());
        goods.setCategory2Name(baseCategoryView.getCategory2Name());
        goods.setCategory3Id(baseCategoryView.getCategory3Id());
        goods.setCategory3Name(baseCategoryView.getCategory3Name());
        //查询平台属性集合信息
        List<SkuAttrValue> skuAttrValueList = productFeignClient.getSkuAttrValue(skuId);
        //stream流转化成目标集合
        List<SearchAttr> searchAttrList = skuAttrValueList.stream().map(skuAttrValue -> {
            SearchAttr searchAttr = new SearchAttr();
            searchAttr.setAttrId(skuAttrValue.getBaseAttrInfo().getId());
            searchAttr.setAttrName(skuAttrValue.getBaseAttrInfo().getAttrName());
            searchAttr.setAttrValue(skuAttrValue.getBaseAttrValue().getValueName());
            return searchAttr;
        }).collect(Collectors.toList());
        goods.setAttrs(searchAttrList);
        //添加入索引库中
        goodsDao.save(goods);


    }

    //增加热度评分
    @Override
    public void incrHotScore(Long skuId, Integer score) {
        String hotScore = "hotScore";
        //用缓存保存分数
        Double incrementScore = redisTemplate.opsForZSet().incrementScore(hotScore, skuId, score);
        if (incrementScore % 10 == 0) {
            //当分数达到10的倍数再进行修改es的评分
            //先根据skuId查询商品
            Optional<Goods> optional = goodsDao.findById(skuId);
            Goods goods = optional.get();
            //修改评分
            goods.setHotScore(Math.round(incrementScore));
            //id对应有值:先删除再保存,没有直接保存
            //修改
            goodsDao.save(goods);
        }
    }

    //根据查询参数对象查询商品列表
    @Override
    public SearchResponseVo search(SearchParam searchParam) {

        SearchRequest searchRequest = getSearchRequest(searchParam);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchResponseVo searchResponseVo = parseSearchResponse(searchResponse);
            //总页数
            Long totalPages = (searchResponseVo.getTotal() + searchParam.getPageSize() - 1) / searchParam.getPageSize();
            searchResponseVo.setTotalPages(totalPages);
            searchResponseVo.setPageNo(searchParam.getPageNo());
            searchResponseVo.setPageSize(searchParam.getPageSize());
            return searchResponseVo;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    private SearchRequest getSearchRequest(SearchParam searchParam) {
        //构建请求对象
        SearchRequest searchRequest = new SearchRequest("goods");
        //创建请求体对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //关键词
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", keyword));
        }
        // 品牌id
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            String[] strings = trademark.split(":");
            boolQueryBuilder.filter(QueryBuilders.termQuery("tmId", strings[0]));
        }

        //平台属性
        //props=23:4G:运行内存 平台属性id : 平台属性值: 平台属性
        String[] props = searchParam.getProps();
        if (props != null && props.length > 0) {
           BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); //方式一;使用bool查询装下平台属性的所有过滤条件(装大量儿子)
            for (String prop : props) {
                String[] split = prop.split(":");
                if (split != null && split.length == 3) {
                    //每个儿子
                    BoolQueryBuilder subBoolQuery = QueryBuilders.boolQuery()
                            .must(QueryBuilders.termQuery("attrs.attrId", split[0]))
                            .filter(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                    //方式二:boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs", subBoolQuery, ScoreMode.None));
                    boolQuery.must(QueryBuilders.nestedQuery("attrs", subBoolQuery, ScoreMode.None));
                }
            }
            //组装平台属性条件
            boolQueryBuilder.filter(boolQuery);
        }

        // 三级分类
        Long category1Id = searchParam.getCategory1Id();
        if (category1Id != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", category1Id));
        }
        Long category2Id = searchParam.getCategory2Id();
        if (category2Id != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", category2Id));
        }
        Long category3Id = searchParam.getCategory3Id();
        if (category3Id != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", category3Id));
        }
        searchSourceBuilder.query(boolQueryBuilder);
        //排序
        // order=1:asc  排序规则   0:asc 1：综合排序/热点  2：价格
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] strings = StringUtils.split(order,":");
            String sortValue = "";
            switch (strings[0]) {
                case "1":
                    sortValue = "hotScore";
                    break;
                case "2":
                    sortValue = "price";
            }
            searchSourceBuilder.sort(sortValue, "asc".equalsIgnoreCase(strings[1]) ? SortOrder.ASC : SortOrder.DESC);
        } else {
            //无排序 走默认综合排序 DESC
            searchSourceBuilder.sort("hotScore", SortOrder.DESC);
        }
        //获取高亮title
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font style='color:red'>");
        highlightBuilder.postTags("</font>");
        highlightBuilder.field("title");
        searchSourceBuilder.highlighter(highlightBuilder);
        //分页
        Integer pageNo = searchParam.getPageNo();
        Integer pageSize = searchParam.getPageSize();
        searchSourceBuilder.from((pageNo - 1) * pageSize).size(pageSize);

        //品牌聚合,品牌id和品牌名称和图片url是一一对应的关系
        // 将品牌名称和品牌url挂载到id下，方便解析品牌信息返回给页面   (TODO 难点)
        searchSourceBuilder.aggregation(AggregationBuilders.terms("tmIdAgg").field("tmId")
                .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"))
        );

        //构建平台属性桶聚合
        searchSourceBuilder.aggregation(
                AggregationBuilders.nested("attrsAgg", "attrs")
                        .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                                .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                                .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))
                        ));

        //打印请求体Json字符串
        System.out.println(searchSourceBuilder);

        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

    private SearchResponseVo parseSearchResponse(SearchResponse searchResponse) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        //　解析品牌
        Aggregations aggregations = searchResponse.getAggregations();
        ParsedLongTerms tmIdAgg = aggregations.get("tmIdAgg");
        //桶数据
        List<? extends Terms.Bucket> buckets = tmIdAgg.getBuckets();
        List<SearchResponseTmVo> trademarkList = buckets.stream().map(bucket -> {
            SearchResponseTmVo searchResponseTmVo = new SearchResponseTmVo();
            //组装tmId
            long tmId = bucket.getKeyAsNumber().longValue();
            searchResponseTmVo.setTmId(tmId);
            //组装tmName
            ParsedStringTerms tmNameAgg = bucket.getAggregations().get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
            searchResponseTmVo.setTmName(tmName);
            //组装tmLogoUrl
            ParsedStringTerms tmLogoUrlAgg = bucket.getAggregations().get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            searchResponseTmVo.setTmLogoUrl(tmLogoUrl);
            return searchResponseTmVo;
        }).collect(Collectors.toList());
        //组装品牌列表
        searchResponseVo.setTrademarkList(trademarkList);
        // 解析平台属性
       // List<SearchResponseAttrVo> attrsList
        ParsedNested attrsAgg = aggregations.get("attrsAgg");
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attrIdAgg");
        List<SearchResponseAttrVo> attrsList  =  attrIdAgg.getBuckets().stream().map(bucket->{
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
            //组装attrId
            long attrId = bucket.getKeyAsNumber().longValue();
            searchResponseAttrVo.setAttrId(attrId);
            //组装attrName
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            searchResponseAttrVo.setAttrName(attrName);
            //组装attrValueList
            ParsedStringTerms  attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            List<String> attrValueList  = attrValueAgg.getBuckets().stream().map(b->{
                String keyAsString = b.getKeyAsString();
                return keyAsString;
            }).collect(Collectors.toList());
            searchResponseAttrVo.setAttrValueList(attrValueList);
            return searchResponseAttrVo;
        }).collect(Collectors.toList());
        //组装平台属性集合对象
        searchResponseVo.setAttrsList(attrsList);
        SearchHits hits = searchResponse.getHits();
        SearchHit[] hits1 = hits.getHits();
        //商品列表
        List<Goods> goodsList = Arrays.stream(hits1).map(hit -> {
            String hitSourceAsString = hit.getSourceAsString();
          /*  ObjectMapper objectMapper = new ObjectMapper();
            Goods goods =null;
            try {
                goods = objectMapper.readValue(hitSourceAsString, Goods.class);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }*/ //ObjectMapper 的一种转换方式
            Goods goods = JSONObject.parseObject(hitSourceAsString, Goods.class);
            //解析高亮
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields != null && highlightFields.size() > 0) {
                HighlightField title = highlightFields.get("title");
                String h = title.fragments()[0].toString();
                goods.setTitle(h);
            }
            return goods;
        }).collect(Collectors.toList());
        // 分页
        //总记录数
        long total = hits.getTotalHits();
        searchResponseVo.setTotal(total);
        searchResponseVo.setGoodsList(goodsList);
        return searchResponseVo;
    }
}
